/***************************    WARNING    **********************************/
/* Do not modify the code below these markers.
 * All the code is JSwarm Engine code and must remain  
 * Separate from all actual game code!!!
 * 
	/****************************************************************************/

package main;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JPanel;
import performanceProfiling.PerformanceProfiling;
import stateManager.StateManager;
import networking.MultiPlayer;
import renderStrategy.RenderStrategy;
import propertyManager.*;

@SuppressWarnings("serial")
public class Board extends JPanel implements Commons,Runnable{ 


	private Thread animator;
	private long animatorId;
	private Thread repaint;
	private long repaintId;
	ExecutorService multiPlayerExecSvc;
	private Dimension d;

	protected TAdapter userInput;
	private PerformanceProfiling profiles;
	private boolean debug;
	private boolean cycleA, cycleB;
	
	
	public GameTree gameTree;
	public StateManager stateManager;
	public boolean useStateManager = false;
	
	public MultiPlayer multiPlayer;
	private RenderStrategy renderStrategy;
	private PropertyManager propertyManager;
	
	

	
	public Board(boolean useStateManager) 
	{	
		this.propertyManager= PropertyManager.getInstance();
		this.useStateManager = useStateManager;
		this.setVisible(true);
		gameTree = new GameTree();
		if(useStateManager){
			stateManager = StateManager.getInstance();
		}
		userInput = new TAdapter();
		addKeyListener(userInput);
		addMouseListener(userInput);
		addMouseMotionListener(userInput);
		multiPlayerExecSvc = Executors.newFixedThreadPool(2);
		setFocusable(true);
		d = new Dimension((Integer)propertyManager.find("BOARD_WIDTH"),
				(Integer)propertyManager.find("BOARD_HEIGTH"));
		this.setSize(d);
		setBackground(Color.black);
		cycleA = true;
		cycleB = false;	
		profiles = new PerformanceProfiling();
		profiles.showAll();
		renderStrategy = new RenderStrategy();
		//multiPlayer = new MultiPlayer(9999, "2ua1010y62");
				
	}

	
	public Board(int width, int height, Color bgColor) 
	{	
		this.propertyManager= PropertyManager.getInstance();
		this.setVisible(true);
		gameTree = new GameTree();
		if(useStateManager){
			stateManager = StateManager.getInstance();
		}
		userInput = new TAdapter();
		addKeyListener(userInput);
		addMouseListener(userInput);
		addMouseMotionListener(userInput);
		multiPlayerExecSvc = Executors.newFixedThreadPool(2);
		setFocusable(true);
		d = new Dimension(width, height);
		this.setSize(d);
		setBackground(bgColor);
		cycleA = true;
		cycleB = false;	
		profiles = new PerformanceProfiling();
		profiles.showAll();
		renderStrategy = new RenderStrategy();
		//multiPlayer = new MultiPlayer(9999, "2ua1010y62");
	
	}

	public void startEngine(){
		if (animator == null) {
			animator = new Thread(this);
			animatorId = animator.getId();
			animator.start();
		}
		if (repaint == null) {
			repaint = new Thread(this);
			repaintId = repaint.getId();
			repaint.start();
		}		
	}
		
	public void setDebug(boolean debugValue) {
		this.debug = debugValue;
		gameTree.setDebug(debugValue);
	}
	
	public Dimension getD() {
		return d;
	}

	public void addNotify() {
		super.addNotify();
	}
	
	protected void setCursorImage(String imageLocation) {
		this.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(
				Toolkit.getDefaultToolkit().getImage(imageLocation), new Point(0,0), "Custom Cursor"));
	}

	public void handleInput(){
		//Function not needed.  Only as a place holder
		//is referenced in animationCycle but is never called due to
		//boards that extend need to write this function themselves
		try{
			//gameHandleInput();
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}

	public void paint(Graphics g){
		super.paint(g);
	}
	public void animationCycle()  {
		if(useStateManager){
			stateManager.changeToUpcomingState();
			if(!stateManager.isCurrentStateInitialized()){
				this.gameTree = stateManager.initState(gameTree);
			}			
			if(stateManager.didChangeStates()){
				this.gameTree = stateManager.initState(gameTree);
			}
			this.gameTree = stateManager.getCurrentState().handleInput(userInput);
		}
		else{
			handleInput();
		}
		if(cycleA){
			gameTree.thinkEverything(cycleA);
		}
		else{
			gameTree.thinkEverything(cycleA);
		}		
	}

	public synchronized void notifyReDraw(){		
		renderStrategy.notifyReDraw(gameTree);
	}
	public void run() {
		if(Thread.currentThread().getId() == repaintId){			
				System.out.println("here1");
				renderStrategy.render(cycleA, this,profiles, debug );				
		}
		
		if(Thread.currentThread().getId() == animatorId){
			
			long beforeTime, timeDiff, sleep;
			beforeTime = System.currentTimeMillis();
			while(true){
				notifyReDraw();	
				try{	
					if(debug){
						profiles.incrementAnimationCycle();
						profiles.fpsStart();
						profiles.thinkTimeStart();
					}
					animationCycle();
					if(debug){
						profiles.thinkTimeStop();
						profiles.fpsStop();
					}
					
				}catch(Exception e)	{
					e.printStackTrace();
				}
				if(debug){
					profiles.drawTimeStart();
				}		
				if(debug){
					profiles.drawTimeStop();
				}
				timeDiff = System.currentTimeMillis() - beforeTime;
				sleep = DELAY - timeDiff;
	
				if (sleep < 0) sleep = 2;
				try{								
					Thread.sleep(sleep);
					cycleA = !cycleA;
					cycleB = !cycleB;
					
				} catch (InterruptedException e) {
					System.out.println("interrupted");
				}
				beforeTime = System.currentTimeMillis();
			}
		}
	}		
}
